/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2013 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#ifndef __D3D9Device_H__
#define __D3D9Device_H__

#include "OgreD3D9Prerequisites.h"
#include "OgreRenderTarget.h"

namespace Ogre {

	class D3D9RenderWindow;
	class D3D9DeviceManager;

	/** High level interface of Direct3D9 Device.
	Provide useful methods for device handling.
	*/
	class _OgreD3D9Export D3D9Device : public ResourceAlloc
	{

	// Interface.
	public:
		void					attachRenderWindow		(D3D9RenderWindow* renderWindow);
		void					detachRenderWindow		(D3D9RenderWindow* renderWindow);
	
		bool					acquire					();
		
		void					release					();		
		void					destroy					();		
		
		bool					isDeviceLost			();				
		IDirect3DDevice9*		getD3D9Device			();
					
		UINT					getAdapterNumber		() const;
		D3DDEVTYPE				getDeviceType			() const;
		bool					isMultihead				() const;					
		bool					isAutoDepthStencil		() const;
		bool					isFullScreen			() const;
		
		const D3DCAPS9&			getD3D9DeviceCaps		() const;
		D3DFORMAT				getBackBufferFormat		() const;

		bool					validate				(D3D9RenderWindow* renderWindow);
		void					invalidate				(D3D9RenderWindow* renderWindow);

		void					present					(D3D9RenderWindow* renderWindow);
		
		IDirect3DSurface9*		getDepthBuffer			(D3D9RenderWindow* renderWindow);
		IDirect3DSurface9*		getBackBuffer			(D3D9RenderWindow* renderWindow);

		uint					getRenderWindowCount	() const;
		D3D9RenderWindow*		getRenderWindow			(uint index);
		uint					getLastPresentFrame		() const { return mLastPresentFrame; }

		void					setAdapterOrdinalIndex  (D3D9RenderWindow* renderWindow, uint adapterOrdinalInGroupIndex);
		void					copyContentsToMemory(D3D9RenderWindow* window, const PixelBox &dst, RenderTarget::FrameBuffer buffer);
		void					clearDeviceStreams		();
	
	public:
		D3D9Device	(D3D9DeviceManager* deviceManager,
					 UINT adapterNumber, 
					 HMONITOR hMonitor, 
					 D3DDEVTYPE devType, 
					 DWORD behaviorFlags);
		~D3D9Device	();

	protected:			
		D3D9DeviceManager*				mDeviceManager;			    // The manager of this device instance.
		IDirect3DDevice9*				mDevice;					// Will hold the device interface.				
		UINT							mAdapterNumber;				// The adapter that this device belongs to.	
		HMONITOR						mMonitor;					// The monitor that this device belongs to.
		D3DDEVTYPE						mDeviceType;				// Device type.	
		static HWND						msSharedFocusWindow;		// The shared focus window in case of multiple full screen render windows.
		HWND							mFocusWindow;				// The focus window this device attached to.			
		DWORD							mBehaviorFlags;				// The behavior of this device.		
		D3DPRESENT_PARAMETERS*			mPresentationParams;		// Presentation parameters which the device was created with. May be
																	// an array of presentation parameters in case of multi-head device.				
		UINT							mPresentationParamsCount;	// Number of presentation parameters elements.		
		D3DCAPS9						mD3D9DeviceCaps;			// Device caps.	
		bool							mD3D9DeviceCapsValid;		// True if device caps initialized.				
		D3DDEVICE_CREATION_PARAMETERS	mCreationParams;			// Creation parameters.
		uint							mLastPresentFrame;			// Last frame that this device present method called.
		bool							mDeviceLost;				// True if device entered lost state.
	
		struct RenderWindowResources
		{
			IDirect3DSwapChain9* 	swapChain;						// Swap chain interface.
			uint					adapterOrdinalInGroupIndex;		// Relative index of the render window in the group.
			uint					presentParametersIndex;			// Index of present parameter in the shared array of the device.
			IDirect3DSurface9*	 	backBuffer;						// The back buffer of the render window.
			IDirect3DSurface9*	 	depthBuffer;					// The depth buffer of the render window.
			D3DPRESENT_PARAMETERS	presentParameters;				// Present parameters of the render window.
			bool					acquired;						// True if resources acquired.			
		};		
		typedef map<D3D9RenderWindow*, RenderWindowResources*>::type RenderWindowToResourcesMap;
		typedef RenderWindowToResourcesMap::iterator				 RenderWindowToResourcesIterator;

		RenderWindowToResourcesMap mMapRenderWindowToResources;		// Map between render window to resources.


	protected:
		RenderWindowToResourcesIterator getRenderWindowIterator (D3D9RenderWindow* renderWindow);

		bool					acquire							(D3D9RenderWindow* renderWindow);
		bool					reset							();
		void					updatePresentationParameters	();
		void					updateRenderWindowsIndices		();
		
		void					createD3D9Device				();
		void					releaseD3D9Device				();
		void					releaseRenderWindowResources	(RenderWindowResources* renderWindowResources);
		void					acquireRenderWindowResources	(RenderWindowToResourcesIterator it);		
		void					setupDeviceStates				();
		void					notifyDeviceLost				();

		void					validateFocusWindow				();
		void					validateBackBufferSize			(D3D9RenderWindow* renderWindow);
		bool					validateDisplayMonitor			(D3D9RenderWindow* renderWindow);
		bool					validateDeviceState				(D3D9RenderWindow* renderWindow);
		bool					isSwapChainWindow				(D3D9RenderWindow* renderWindow);
		D3D9RenderWindow*		getPrimaryWindow				();
		void					setSharedWindowHandle			(HWND hSharedHWND);

	private:
		friend class D3D9DeviceManager;
		friend class D3D9RenderSystem;
	};
}
#endif
